// Copyright 2017 The Cockroach Authors.
// Copyright (c) 2022-present, Shanghai Yunxi Technology Co, Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This software (KWDB) is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package sql

import "gitee.com/kwbasedb/kwbase/pkg/sql/sqlbase"

// ReqOrdering is the ordering that must be preserved by an operator when it is
// distributed. It is used to configure DistSQL with the orderings it needs to
// maintain when joining streams.
type ReqOrdering = sqlbase.ColumnOrdering

// planReqOrdering describes known ordering information for the rows generated by
// this node. The ordering information includes columns the output is ordered
// by and columns for which we know all rows have the same value.
func planReqOrdering(plan planNode) ReqOrdering {
	switch n := plan.(type) {
	case *explainPlanNode:
		return planReqOrdering(n.run.results)
	case *limitNode:
		return planReqOrdering(n.plan)
	case *max1RowNode:
		return planReqOrdering(n.plan)
	case *spoolNode:
		return planReqOrdering(n.source)
	case *saveTableNode:
		return planReqOrdering(n.source)
	case *serializeNode:
		return planReqOrdering(n.source)
	case *deleteNode:
		if n.run.rowsNeeded {
			return planReqOrdering(n.source)
		}
	case *projectSetNode:
		return n.reqOrdering

	case *filterNode:
		return n.reqOrdering

	case *groupNode:
		return n.reqOrdering

	case *distinctNode:
		return n.reqOrdering

	case *indexJoinNode:
		return n.reqOrdering

	case *windowNode:
		// TODO: window partitions can be ordered if the source is ordered
		// appropriately.
	case *joinNode:
		return n.reqOrdering
	case *unionNode:
		// TODO(knz): this can be ordered if the source is ordered already.
	case *insertNode, *insertFastPathNode:
		// TODO(knz): RETURNING is ordered by the PK.
	case *updateNode, *upsertNode:
		// After an update, the original order may have been destroyed.
		// For example, if the PK is updated by a SET expression.
		// So we can't assume any ordering.
		//
		// TODO(knz/radu): this can be refined by an analysis which
		// determines whether the columns that participate in the ordering
		// of the source are being updated. If they are not, the source
		// ordering can be propagated.

	case *scanNode:
		return n.reqOrdering
	case *ordinalityNode:
		return n.reqOrdering
	case *renderNode:
		return n.reqOrdering
	case *sortNode:
		return n.ordering
	case *lookupJoinNode:
		return n.reqOrdering
	case *zigzagJoinNode:
		return n.reqOrdering
	}

	return nil
}
